package com.yixing.tech.common.utils;

import com.yixing.tech.common.vo.s5.PPKObject;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yixing tech
 * @description
 * @date 2023/11/28 14:41:19
 */
public class PPKUtil {

    static double d2_2 = 1.128D;

    static Map<Integer, Double> groupCoefficient = new HashMap<>();

    static {
        groupCoefficient.put(2, d2_2);
        groupCoefficient.put(3, 1.693D);
        groupCoefficient.put(4, 2.059D);
        groupCoefficient.put(5, 2.326D);
    }

    /**
     * 偏离一步，子组为2。
     *
     * @param lsl
     * @param usl
     * @param precision
     * @param data
     * @param step      数据稀疏的间隔
     * @return
     */

    public static Object calculateNormalDistribution(Double lsl, Double usl, Integer precision, List<Double> data) {
        return calculateNormalDistribution(lsl, usl,null,null,null, precision, data, 0);
    }

    public static Object calculateNormalDistribution(Double lsl, Double usl,Double lcl, Double ucl,Double standard, Integer precision, List<Double> data, double step) {
        List<Double> data1 = data.stream().filter(Objects::nonNull).collect(Collectors.toList());
        Collections.sort(data1);
        if (lsl == null) lsl = data1.get(0);
        if (usl == null) usl = data1.get(data1.size() - 1);
        if (precision == null) precision = 1;
        PPKObject ppkObject = new PPKObject();

        ppkObject.setPrecision(precision);
        ppkObject.setN(data1.size());
        ppkObject.setMaxValue(data1.get(data1.size() - 1));
        ppkObject.setMinValue(data1.get(0));
        double aveValue = getAveValue(data);
        String formatStr = "%." + precision + "f";
        ppkObject.setAveValue(String.format(formatStr, aveValue));
        double sigmaLong = Math.pow(data.stream().mapToDouble(item -> Math.pow(item - aveValue, 2)).sum() / data.size(), 0.5);
        ppkObject.setSigmaLong(String.format("%.5f", sigmaLong));
        List<Double> rangeList = new ArrayList<>();
        for (int i = 0; i < data.size() - 1; i++) {
            rangeList.add(Math.abs(data.get(i + 1) - data.get(i)));
        }
        double rangeAveValue = getAveValue(rangeList);
        double sigmaShort = rangeAveValue / d2_2;
        ppkObject.setSigmaShort(String.format("%.5f", sigmaShort));

        //ppk、cpk
        double t = usl - lsl;
        double pp = t / (6 * sigmaLong);
        ppkObject.setPpValue(String.format("%.2f", pp));
        double ppu = (usl - aveValue) / (3 * sigmaLong);
        ppkObject.setPpuValue(String.format("%.2f", ppu));
        double ppl = (aveValue - lsl) / (3 * sigmaLong);
        ppkObject.setPplValue(String.format("%.2f", ppl));
        ppkObject.setPpkValue(String.format("%.2f", Math.min(ppu, ppl)));
        double cp = t / (6 * sigmaShort);
        ppkObject.setCpValue(String.format("%.2f", cp));
        double cpu = (usl - aveValue) / (3 * sigmaShort);
        ppkObject.setCpuValue(String.format("%.2f", cpu));
        double cpl = (aveValue - lsl) / (3 * sigmaShort);
        ppkObject.setCplValue(String.format("%.2f", cpl));
        ppkObject.setCpkValue(String.format("%.2f", Math.min(cpu, cpl)));

        /**
         * 正太分布
         */
        //数据统一精度处理
        List<String> list = data.stream().map(e -> String.format(formatStr, e)).collect(Collectors.toList());


        Collections.sort(list, (o1, o2) -> Double.valueOf(o1).compareTo(Double.parseDouble(o2)));
        //按步长稀疏
        if (step != 0) {
            int midIndex = list.size() / 2;
            String tempF = list.get(midIndex);
            String tempB = list.get(midIndex);
            for (int i = midIndex - 1; i >= 0; i--) {
                String v = list.get(i);
                if (Double.parseDouble(tempF) - Double.parseDouble(v) <= step) {
                    list.set(i, tempF);
                } else {
                    tempF = v;
                }
            }
            for (int i = midIndex + 1; i < list.size(); i++) {
                String v = list.get(i);
                if (Double.parseDouble(v) - Double.parseDouble(tempB) <= step) {
                    list.set(i, tempB);
                } else {
                    tempB = v;
                }
            }
        }
        //频次
        Map<String, Long> map = list.stream().collect(Collectors.groupingBy(e -> e, Collectors.counting()));
        //x轴
        List<String> xList = new ArrayList<>(map.keySet());
        String uslStr = String.format(formatStr, usl);
        String lslStr = String.format(formatStr, lsl);
        if(!ObjectUtils.isEmpty(ucl)){
            String uclStr = String.format(formatStr, ucl);
            ppkObject.setUcl(uclStr);
            if (!xList.contains(uclStr)) xList.add(uclStr);
        }
        if(!ObjectUtils.isEmpty(lcl)){
            String lclStr = String.format(formatStr, lcl);
            ppkObject.setLcl(lclStr);
            if (!xList.contains(lclStr)) xList.add(lclStr);
        }
        if(!ObjectUtils.isEmpty(standard)){
            String standardStr = String.format(formatStr, standard);
            ppkObject.setStandard(standardStr);
            if (!xList.contains(standardStr)) xList.add(standardStr);
        }

        String aveValueStr = String.format(formatStr, aveValue);
        if (!xList.contains(uslStr)) xList.add(uslStr);
        if (!xList.contains(lslStr)) xList.add(lslStr);
        if (!xList.contains(aveValueStr)) xList.add(aveValueStr);
        ppkObject.setLsl(lslStr);
        ppkObject.setUsl(uslStr);


        Collections.sort(xList, (o1, o2) -> Double.valueOf(o1).compareTo(Double.parseDouble(o2)));
        List<Long> yList = xList.stream().map(map::get).collect(Collectors.toList());
        //概率
        List<Double> zList = xList.stream().map(m -> {
            double y = (1 / (sigmaLong * Math.sqrt(2 * Math.PI))) * Math.exp(-Math.pow((Double.parseDouble(m) - aveValue), 2) / (2 * Math.pow(sigmaLong, 2)));
            return Double.parseDouble(String.format("%.2f", y));
        }).collect(Collectors.toList());
        ppkObject.setXList(xList);
        ppkObject.setYList(yList);
        ppkObject.setZList(zList);
        return ppkObject;
    }

    /**
     * @param lsl       下限
     * @param usl       上限
     * @param precision 精度
     * @param data      数据集
     * @param groupNum  子组大小
     * @return
     */
    public static Object calculateNormalDistributionByGroup(Double lsl, Double usl, Integer precision, List<Double> data, int groupNum) {
        if (ObjectUtils.isEmpty(data)) return null;
        int mod = data.size() % groupNum;
        data = data.subList(0, data.size() - mod);
        if (ObjectUtils.isEmpty(data)) return null;
        List<Double> data1 = data.stream().filter(Objects::nonNull).collect(Collectors.toList());
        Collections.sort(data1);
        if (lsl == null) lsl = data1.get(0);
        if (usl == null) usl = data1.get(data1.size() - 1);
        if (precision == null) precision = 1;
        PPKObject ppkObject = new PPKObject();
        ppkObject.setLsl(lsl);
        ppkObject.setUsl(usl);
        ppkObject.setPrecision(precision);
        ppkObject.setN(data.size());
        ppkObject.setMaxValue(data1.get(data.size() - 1));
        ppkObject.setMinValue(data1.get(0));
        double aveValue = getAveValue(data);
        ppkObject.setAveValue(String.format("%.5f", aveValue));
        double sigmaLong = Math.pow(data.stream().mapToDouble(item -> Math.pow(item - aveValue, 2)).sum() / data.size(), 0.5);
        ppkObject.setSigmaLong(String.format("%.5f", sigmaLong));
        List<Double> rangeList = new ArrayList<>();
        for (int i = 0; i < data.size() - groupNum + 1; i += groupNum) {
            rangeList.add(Math.abs(data.get(i + groupNum - 1) - data.get(i)));
        }
        double rangeAveValue = getAveValue(rangeList);
        double sigmaShort = rangeAveValue / groupCoefficient.get(groupNum);
        ppkObject.setSigmaShort(String.format("%.5f", sigmaShort));

        //ppk、cpk
        double t = usl - lsl;
        double pp = t / (6 * sigmaLong);
        ppkObject.setPpValue(String.format("%.2f", pp));
        double ppu = (usl - aveValue) / (3 * sigmaLong);
        ppkObject.setPpuValue(String.format("%.2f", ppu));
        double ppl = (aveValue - lsl) / (3 * sigmaLong);
        ppkObject.setPplValue(String.format("%.2f", ppl));
        ppkObject.setPpkValue(String.format("%.2f", Math.min(ppu, ppl)));
        double cp = t / (6 * sigmaShort);
        ppkObject.setCpValue(String.format("%.2f", cp));
        double cpu = (usl - aveValue) / (3 * sigmaShort);
        ppkObject.setCpuValue(String.format("%.2f", cpu));
        double cpl = (aveValue - lsl) / (3 * sigmaShort);
        ppkObject.setCplValue(String.format("%.2f", cpl));
        ppkObject.setCpkValue(String.format("%.2f", Math.min(cpu, cpl)));

        /**
         * 正太分布
         */
        //数据统一精度处理
        String formatStr = "%." + precision + "f";
        List<String> list = data.stream().map(e -> String.format(formatStr, e)).collect(Collectors.toList());
        //频次
        Map<String, Long> map = list.stream().collect(Collectors.groupingBy(e -> e, Collectors.counting()));
        //x轴
        List<String> xList = new ArrayList<>(map.keySet());
        Collections.sort(xList, (o1, o2) -> Double.valueOf(o1).compareTo(Double.parseDouble(o2)));
        List<Long> yList = xList.stream().map(map::get).collect(Collectors.toList());
        //概率
        List<Double> zList = xList.stream().map(m -> {
            double y = (1 / (sigmaLong * Math.sqrt(2 * Math.PI))) * Math.exp(-Math.pow((Double.parseDouble(m) - aveValue), 2) / (2 * Math.pow(sigmaLong, 2)));
            return Double.parseDouble(String.format("%.4f", y));
        }).collect(Collectors.toList());
        ppkObject.setXList(xList);
        ppkObject.setYList(yList);
        ppkObject.setZList(zList);
        return ppkObject;
    }

    public static double getAveValue(List<Double> data) {
        return data.stream().mapToDouble(Double::doubleValue).average().orElse(0D);
    }


}
